package edu.xidian.onlinedocument.utils.ot.internal;

import org.apache.commons.lang3.ArrayUtils;

import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

import static java.lang.System.arraycopy;

public class GapBuffer {

    private List<Character> text;
    /*
    // An operation's baseLength is the length of every string the operation
    // can be applied to.
    protected int baseLength = 0;
    // The targetLength is the length of every string that results from applying
    // the operation on a valid input string.
    protected int targetLength = 0;
     */
    private boolean isOpen = false;
    private int charIndex = 0;

    public GapBuffer(String str) {
        this(str.toCharArray());
    }

    public GapBuffer(char[] str) {
        this(ArrayUtils.toObject(str));
    }

    public GapBuffer(Character[] str) {
        this(new LinkedList<>(Arrays.asList(str)));
    }

    public GapBuffer(List<Character> text) {
        this.text = text;
    }

    // 开启buffer，允许对文档内容进行操作
    public void open() {
        // baseLength = 0;
        // targetLength = 0;
        this.isOpen = true;
    }

    // 关闭buffer，不允许对文档内容进行操作，并初始化charIndex
    public void close() {
        this.isOpen = false;
        this.charIndex = 0;
    }

    public void clean() {
        text.clear();
    }

    public int size() {
        return text.size();
    }

    public boolean retain(int length) {
        if(!checkOpen()) {
            return false;
        }
        System.out.println("retain, size=" + size() + ", charIndex=" + charIndex + ", length=" + length);
        if(length < 0) {
            throw new IllegalArgumentException("retain: length小于0");
        }
        // length+charIndex可以等于size()
        // 如文本"abcd"，其一共有0'a'1'b'2'c'3'd'4，即共5个位置，而此时size=4，若charIndex=0时length=4，则length+charIndex=4是被允许的
        // 此时应该在处理insert的时候要考虑charIndex等于size的情况
        if(length + charIndex > size()) {
            throw new IllegalArgumentException("retain: length大于文本长度");
        }
        // this.baseLength += length;
        // this.targetLength += length;
        charIndex += length;
        return true;
    }

    public boolean insert(String str) {
        if(!checkOpen()) {
            return false;
        }
        System.out.println("insert, size=" + size() + ", charIndex=" + charIndex + ", string=" + str);
        // this.targetLength += str.length();
        Character[] chars = ArrayUtils.toObject(str.toCharArray());
        for (Character c:
             chars) {
            // 注意list.add(index, e)方法会将元素e插入到index的位置，而原来的元素则会放到index+1的位置
            // 因此retain的length，即charIndex和index应该是对应的
            // 如果list为空，则直接插入char
            if (size() == 0) {
                text.add(c);
            }
            // 如果charIndex指针指向已有字符串最末尾的字符的下一位，则也直接插入char，并且使charIndex+1
            // 此时如果没有再执行retain，则接下来的字符串都会执行这个if的代码
            else if (charIndex == size()) {
                text.add(c);
            }
            // 否则add到指定指针的位置，即retain的值决定的charIndex位置
            else {
                text.add(charIndex, c);
            }
            charIndex++;
        }
        return true;
    }

    public boolean delete(int length) {
        if(!checkOpen()) {
            return false;
        }
        System.out.println("delete, size=" + size() + ", charIndex=" + charIndex + ", length=" + length);
        if(length < 0) {
            throw new IllegalArgumentException("delete: length小于0");
        }
        // this.baseLength -= length;
        for (int i = 0; i < length; i++) {
            text.remove(charIndex);
        }
        return true;
    }

    private boolean checkOpen() {
        return isOpen;
    }

    public String build() {
        StringBuffer result = new StringBuffer();
        for (Character c:
             text) {
            result.append(c);
        }
        return result.toString();
    }
}
